package math2;

/**
 * A function from unit cube in n dimension to R
 * 
 * @author hbui
 */
public abstract class FunctionRnToR {

	/**
	 * Computes the gradient [df/dxi1, df/dxi2, ...]^T.
	 * 
	 * @param x
	 *            vector of natural coordinates
	 * @return gradient (array of same length with xi)
	 */
	public abstract double[] gradientAt(double... x);

	/**
	 * Computes the function value at the specified point.
	 * 
	 * @param x
	 *            vector of natural coordinates
	 * @return functions value
	 */
	public abstract double valueAt(double... x);

	/**
	 * Computes the sum function f = alpha * this + beta * v
	 * 
	 * @param: alpha: scalar value beta: scalar value v: a function
	 * @return the sum function
	 */
	public FunctionRnToR add(double alpha, double beta, FunctionRnToR v) {
		if (beta == 0 || v instanceof NullFunctionRnToR) {
			if (alpha == 0) {
				return new NullFunctionRnToR();
			} else {
				return this.multiply(alpha);
			}
		} else {
			if (alpha == 0) {
				return v.multiply(beta);
			}
		}
		return new SumFunctionRnToR(alpha, this, beta, v);
	}

	/**
	 * Multiply the function with a scalar value
	 * 
	 * @param: alpha
	 * @return alpha*this
	 */
	public FunctionRnToR multiply(double alpha) {
		if (alpha == 0) {
			return new NullFunctionRnToR();
		}
		if (alpha == 1) {
			return this;
		}
		return new ScalarMultFunctionRnToR(alpha, this);
	}

	/**
	 * Multiply the function with a scalar value and another function
	 * 
	 * @param alpha
	 * @param v
	 * @return f = alpha * v * this function
	 */
	public FunctionRnToR multiply(double alpha, FunctionRnToR v) {
		if (alpha == 0 || v instanceof NullFunctionRnToR) {
			return new NullFunctionRnToR();
		}
		return new MultFunctionRnToR(alpha, this, v);
	}

	/**
	 * get the associated degree if this function have polynomial form.
	 * Otherwise the degree will be -1
	 * 
	 * @return p
	 */
	public int getP() {
		return -1;
	}
}
